#pragma once
/**
    MTree distance-based indexing structure
    Copyright (C) 2022  David G. Starkweather starkdg@gmx.com

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

**/
#include <cstdint>
#include <algorithm>
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
namespace MetricSpaceBenchmark::MetricIndex::MTree {

    /*
     * 条目信息: id + key
     */
    template<typename T>
    class Entry {
    public:
        Entry() = default;

        Entry(const long long id, const T key) : id(id), key(key) {};

        Entry(const Entry<T> &other) {
            id = other.id;
            key = other.key;
        }

        Entry<T> &operator=(const Entry<T> &other) {
            id = other.id;
            key = other.key;
            return *this;
        }

        long long id{};
        T key;
    };
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /*
     * 条目信息: id + key + subtree(子树) + cover_radius(覆盖半径) + dis(距离)
     */
    template<typename T>
    class RoutingObject {
    public:
        RoutingObject() : id(0), subtree(nullptr) {}

        RoutingObject(const long long id, const T key) : id(id), key(key), subtree(nullptr), cover_radius(0),
                                                         dis(0) {}

        RoutingObject(const RoutingObject &other) {
            id = other.id;
            key = other.key;
            subtree = other.subtree;
            cover_radius = other.cover_radius;
            dis = other.dis;
        }

        RoutingObject &operator=(const RoutingObject &other) {
            if (this != &other) { // 检查自赋值
                id = other.id;
                key = other.key;
                subtree = other.subtree;
                cover_radius = other.cover_radius;
                dis = other.dis;
            }
            return *this;
        }

        // 计算当前对象与另一个对象的距离
        double distance(const T &other) const {
            // 计数操作次数
            RoutingObject<T>::n_build_ops++;
            return key.distance(other);    // 调用 `key` 对象的 `distance` 方法
        }

        static unsigned long n_build_ops;    // 静态成员变量, 记录操作次数
        long long id;
        T key;
        void *subtree;
        double cover_radius{};
        double dis{};
    };
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /*
     * 条目信息: id + key +dis
     */
    template<typename T>
    class DBEntry {
    public:
        DBEntry() = default;

        DBEntry(const long long id, const T key, const double dis) : id(id), key(key), dis(dis) {}

        DBEntry(const DBEntry &other) {
            id = other.id;
            key = other.key;
            dis = other.dis;
        }

        DBEntry &operator=(const DBEntry &other) {
            id = other.id;
            key = other.key;
            dis = other.dis;
            return *this;
        }

        double distance(const T &other) const {
            DBEntry<T>::n_query_ops++;
            return key.distance(other);
        }

        static unsigned long n_query_ops;

        long long id{};
        T key;
        double dis{};
    };

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    template<typename T>
    unsigned long MTree::RoutingObject<T>::n_build_ops = 0;

    template<typename T>
    unsigned long MTree::DBEntry<T>::n_query_ops = 0;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}